package org.bl.chat.websocket;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bl.chat.dao.UserDao;
import org.bl.chat.enums.WebMessageType;
import org.bl.chat.model.dto.MessageDTO;
import org.bl.chat.model.po.MessageDO;
import org.bl.chat.model.po.UserDO;
import org.bl.chat.service.MessageService;
import org.bl.chat.service.UserService;
import org.bl.chat.websocket.dto.MessageWrapper;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Component
public class ChatWebSocketHandler implements WebSocketHandler {

    private final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>());
    @Resource
    private UserService userService;

    @Resource
    private MessageService messageService;

    private Map<String, String> ipMap = new HashMap<>();


    private static final ConcurrentHashMap<String, MessageDO> LEAK_MAP = new ConcurrentHashMap<>();
    private static final AtomicLong LEAK_COUNTER = new AtomicLong();

    @PostConstruct
    public void loadUserMessage(){

        List<UserDO> byIsOnline = userService.findByIsOnline(true);
        ipMap = byIsOnline
                .stream().collect(Collectors.toMap(UserDO::getIp, UserDO::getUsername));
    }



    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
        broadUserList(session);
    }

    /**
     * 原有的 handleMessage，加入“泄漏行为”：
     * 每收到一条消息，都把生成的 MessageDO 放到静态 map 里，且不删除（永不释放）。
     * 同时为了更明显，可以每 N 条打印一次 map 大小。
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
        // 1. 检查消息是否为 TextMessage
        if (!(message instanceof TextMessage)) {
            System.out.println("收到非文本消息，跳过处理。");
            return;
        }
        TextMessage textMessage = (TextMessage) message;
        String payload = textMessage.getPayload();
        try {
            // 注意：前端发送的消息内容必须符合 MessageDTO 的结构
            MessageDTO receivedMessage = JSONObject.parseObject(payload, MessageDTO.class);
            // 尝试从 Session Attributes 中获取（如果在 afterConnectionEstablished 中存储了）
            InetSocketAddress remoteAddress = session.getRemoteAddress();
            if (remoteAddress != null) {
                String ip = receivedMessage.getSenderIp();
                upUserName(receivedMessage, ip);
            }
            // 3.2 补充或覆盖创建时间（以服务器时间为准，防止客户端篡改）
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            receivedMessage.setCreateDate(LocalDateTime.now().format(formatter));
            // 3.3 补充消息ID（如果MessageDTO中的ID是自动生成的，则无需此步）
            MessageDO bean = BeanUtil.toBean(receivedMessage, MessageDO.class);
            // 1. 验证 receivedMessage 的 channelId, sender 等字段
            bean.setId(IdUtil.nanoId());
            messageService.save(bean);
            // 3. 将消息广播给同一 channelId 的所有其他用户
            broadMessageList(session, ListUtil.toList(receivedMessage));

        } catch (Exception e) {
            // 解析失败，可能是前端发送的 JSON 格式错误
            e.printStackTrace();
        }
    }


    /**
     * 更新用户姓名
     * @param receivedMessage
     * @param ip
     * @throws IOException
     */
    private void upUserName(MessageDTO receivedMessage, String ip) throws IOException {
        if(ipMap.containsKey(ip)){
            String dbUserName = ipMap.get(ip);
            if(!dbUserName.equals(receivedMessage.getSender())){
                userService.upUserName(ip, receivedMessage.getSender());
                ipMap.put(ip, receivedMessage.getSender());
                broadAllUser();
            }
        }else{
            // 库表中没有则直接进行更新
            userService.upUserName(ip, receivedMessage.getSender());
            ipMap.put(ip, receivedMessage.getSender());
            // 重新广播用户列表
            broadAllUser();
        }
    }


    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        sessions.remove(session);
        session.close();
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        sessions.remove(session);
        session.close();
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }



    /**
     * 广播用户列表
     */
    private void broadAllUser() throws IOException {
        List<UserDO> byIsOnline = userService.findByIsOnline(true);
        TextMessage textMessage = new TextMessage(new MessageWrapper(WebMessageType.USER_LIST.getType(),
                JSONObject.toJSONString(byIsOnline)).toJsonString());
        for (WebSocketSession session : sessions) {
            session.sendMessage(textMessage);
        }
    }

    /**
     * 广播用户列表
     */
    private void broadUserList(WebSocketSession session) throws IOException {
        List<UserDO> byIsOnline = userService.findByIsOnline(true);
        TextMessage textMessage = new TextMessage(new MessageWrapper(WebMessageType.USER_LIST.getType(),
                JSONObject.toJSONString(byIsOnline)).toJsonString());
        session.sendMessage(textMessage);
    }

    private void broadMessageList(WebSocketSession session, List<MessageDTO> messageDTOS) throws IOException {
        // 将当前程序的后十条信息进行加载
        TextMessage textMessage = new TextMessage(new MessageWrapper(WebMessageType.MESSAGE_LIST.getType(),
                JSONObject.toJSONString(messageDTOS)).toJsonString());
        session.sendMessage(textMessage);
    }


}